package services.api;

import java.util.*;

import controllers.routes;
import dto.api.ApiAuthInfoDto;

import dto.api.ApiInputInfoDto;
import models.pubobj.AniObjectState;
import models.pubobj.AniObjectType;
import models.user.AniUser;
import play.Logger;
import services.application.AniAppService;
import services.exception.*;
import services.security.AniAuthCodeService;
import services.security.AniSecurityService;
import models.application.*;
import services.user.AniLocalUserService;

public class AniApiAuthService extends AniApiService {

	//API authentication
	//-auth info
	public static AniApi checkApiAuthInfoLegality(ApiAuthInfoDto apiAuthInfo) {
        AniSecurityService.checkReqIdLegality(apiAuthInfo.reqId);
        AniApp oneApp = checkAppExistence(apiAuthInfo);
        checkAppAuthInfoLegality(apiAuthInfo, oneApp);
        AniApi targetApi = AniApiService.getApiWithEntity(new ApiInputInfoDto(apiAuthInfo.targetApiId)).get(0);
        checkCallingPrivacyLegality(apiAuthInfo, oneApp, targetApi);
        return targetApi;
    }
    private static AniApp checkAppExistence(ApiAuthInfoDto apiAuthInfo){
        return AniAppService.getApp(new AniApp(apiAuthInfo.clientAppId));
    }
	private static void checkCallingPrivacyLegality(ApiAuthInfoDto apiAuthInfo, AniApp oneApp, AniApi targetApi){
        if(targetApi == null){
            throw new AniDataException("TARGET_API_NOT_EXISTS");
        }
        if(isClientAppReliesOnTargetApiHost(oneApp, targetApi)){
            return;
        }
        //check target APP privacy
        if(targetApi.hostApp.type.typeKey.equals(AniObjectType.ObjectTypes.PRIVATE)){
            AniAppService.checkAppsHaveSameOwnership(new AniApp[]{oneApp, targetApi.hostApp});
        }else if(
                !targetApi.hostApp.type.typeKey.equals(AniObjectType.ObjectTypes.PUBLIC)
                &&!targetApi.hostApp.type.typeKey.equals(AniObjectType.ObjectTypes.PROTECTED)){
            throw new AniDataException("TARGET_APP_TYPE_NOT_EXISTS");
        }
        //check target api privacy
        if(targetApi.type.typeKey.equals(AniObjectType.ObjectTypes.PUBLIC)){
            return;
        }else if(targetApi.type.typeKey.equals(AniObjectType.ObjectTypes.PROTECTED)){
            throw new AniRuleException("PROTECTED_API_IS_ONLY_AVAILABLE_TO_RELIED_APPS");
        }else if(targetApi.type.typeKey.equals(AniObjectType.ObjectTypes.PRIVATE)){
            AniUser oneUser = AniLocalUserService.getCurrentOperatesUser();
            checkUserAppsRelExistence(oneUser, oneApp, targetApi.hostApp);
        }else{
            throw new AniDataException("TARGET_API_TYPE_NOT_EXIST");
        }
	}
    private static void checkAppAuthInfoLegality(ApiAuthInfoDto apiAuthInfo, AniApp srcApp) {
        AniAuthCodeService.checkAuthCodeLegality(
                apiAuthInfo.authCode,
                String.valueOf(apiAuthInfo.clientAppId),
                srcApp.token,
                apiAuthInfo.reqId
        );
	}
    private static Boolean isClientAppReliesOnTargetApiHost(AniApp clientApp, AniApi targetApi){
        if(clientApp.relyOn != null && (clientApp.relyOn.id == targetApi.hostApp.id)){
            return true;
        } else{
            return false;
        }
    }
    private static void checkUserAppsRelExistence(AniUser oneUser, AniApp clientApp, AniApp targetApp){
        if(AniUserAppsRel.findRowCountWithUserAndClientTargetAppObjs(oneUser, clientApp, targetApp) <= 0){
            throw new AniNeedApprovalException(
                    routes.AppApprovalController.userApprovalToTargetApp(clientApp.id, targetApp.id).url()
            );
        }
    }

    //-params legality
	public static void checkApiRequiredParams(AniApi aniApi, Map<String, String> containedParams){
		List<AniApiParam> params = aniApi.params;
		for(AniApiParam oneParam: params){
			if(!containedParams.containsKey(oneParam.paramName)){
				throw new AniAuthException("REQUIRED_PARAMS_CANNOT_BE_FOUND");
			}
		}
	}

    //-approval
    public static void acceptApprovalToTargetApp(Long fromAppId, Long targetAppId){
        Logger.info("=====Start accept user-apps relationship info=====");
        AniUser oneUser = AniLocalUserService.currentLoginUser();
        Logger.info("Current user: " + oneUser.email);
        List<Long> appsId = new ArrayList<Long>();
        appsId.add(fromAppId);
        appsId.add(targetAppId);
        AniAppService.checkAppsExistenceWithIds(appsId);

        saveUserAppsApprovalInfo(oneUser, fromAppId, targetAppId);
    }
    private static void saveUserAppsApprovalInfo(AniUser clientUser, Long fromAppId, Long targetAppId){
        new AniUserAppsRel(
                clientUser,
                new AniApp(fromAppId),
                new AniApp(targetAppId),
                AniObjectState.findByStateKey(
                        AniObjectState.States.ACTIVATED.name()
                )
        );
    }
}